//C++ reflection
//$Id: reflection.h 605 2010-11-27 15:10:07Z Oleg.Bulychov $
#pragma once

#if !defined(_WIN32)
//:strcasecmp
#include <strings.h>
#endif

#include <limits>
#include <vector>
#include "src/heo/include/type_util.h"
#include "src/heo/include/ascii_strcmp.h"

#if !defined(LOG_BAD_DATA)
    #define LOG_BAD_DATA 1
#endif
//__________________________________________________________________________________
#define VAR_(a) (a##_), (STRINGIZE_A(a))
//__________________________________________________________________________________
namespace meta
{
//__________________________________________________________________________________
template<typename T>
struct is_visitable
{
    enum { result = meta::is_class<T>::result };
};
template<>
struct is_visitable<std::string>
{
    enum {result = 0};
};
template<class X>
struct is_visitable< std::vector<X> >
{
    enum {result = 0};
};
//__________________________________________________________________________________
//__________________________________________________________________________________
struct ptr_size
{
    size_t& dim_;
//.............................................................................
    ptr_size(size_t& dim): dim_(dim)
    {}
//.............................................................................
    template<typename T, class V>
    void accept(T* pt, V& v) const
    {
        v.visit_Array(pt, dim_, "", 0);
    }
};
//__________________________________________________________________________________
//:open SPECIALIZATION support e.g. reflection_handler<std::string, V>
template<class T, class V>
struct reflection_handler
{
    template<typename D>
    static void visit(T& t, char const* szName, D const& d, V& v)
    {
        v.visit(t, szName, d);
    }
};
//__________________________________________________________________________________
template<class V>
class visitor
{
    enum
    {
        TMetaType,
        TMetaClass,
        TMetaPointer,
        TMetaArray
    };
protected:
    typedef visitor<V> Base_;
//.............................................................................
    V* self() { return static_cast<V*>(this); }
//.............................................................................
    template<typename T>
    void visit_T(T& t, char const* szName = "")
    {
        return visit_T(t, szName, 0);
    }
//.............................................................................
    template<typename T, typename D>
    void visit_T(T& t, char const* szName, D const& d)
    {
        typedef typename bool2type<is_array<T>::result != 0,
            int2type<TMetaArray>,
            typename bool2type<is_pointer<T>::result != 0,
                int2type<TMetaPointer>,
                typename bool2type<is_visitable<T>::result != 0,
                    int2type<TMetaClass>,
                    int2type<TMetaType>
                >::result
            >::result
        >::result meta;
        visit_T(t, szName, d, meta());
    }
protected:
//.............................................................................
    template<typename T, typename D>
    void visit_T(T& t, char const* szName, D const& d, int2type<TMetaType>)
    {
        reflection_handler<T, V>::visit(t, szName, d, *self());
    }
//.............................................................................
    template<typename T, typename D>
    void visit_T(T& t, char const* szName, D const& d, int2type<TMetaClass>)
    {
        self()->visit_Class(t, szName, d);
    }
//.............................................................................
    template<typename T, typename D>
    void visit_T(T& t, char const* szName, D const& d, int2type<TMetaArray>)
    {
        self()->visit_Array(t, is_array<T>::array_size, szName, d);
    }
//.............................................................................
    template<typename T, typename D>
    void visit_T(T& t, char const* szName, D const& d, int2type<TMetaPointer>)
    {
        self()->visit_Ptr(t, szName, d);
    }
//.............................................................................
public:
//:default handlers
    template<typename T, typename D>
    void visit_Class(T& t, char const* szName, D const& d)
    {
        t.accept(*self());
    }
//.............................................................................
    template<typename T, typename D>
    void visit_Array(T* pt, size_t N, char const* szName, D const& d)
    {
        for (size_t i = 0; i < N; ++i, ++pt)
            visit_T(*pt);
    }
//.............................................................................
    template<typename T, typename D>
    void visit_Ptr(T* pt, char const* szName, D const& d)
    {
        d.accept(pt, *self());
    }
public:
    template<typename T, typename D>
    void operator ()(T& t, char const* szName, D const& d)
    {
        visit_T(t, szName, d);
    }
//.............................................................................
    template<typename T>
    void operator ()(T& t, char const* szName = "")
    {
        visit_T(t, szName, 0);
    }
};
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
inline void String2Value(bool& b, char const* szValue)
{
    b = !ascii_strcmp(szValue, "1") ||
        !ascii_strcmpi(szValue, "true");
}
inline void String2Value(char& ch, char const* szValue)
{
    ch = *szValue;
}
inline void String2Value(int& i, char const* szValue)
{
    i = ::strtol(szValue, NULL, 0);
}
inline void String2Value(unsigned int& u, char const* szValue)
{
    u = ::strtoul(szValue, NULL, 0);
}
inline void String2Value(long& i, char const* szValue)
{
    i = ::strtol(szValue, NULL, 0);
}
inline void String2Value(unsigned long& u, char const* szValue)
{
    u = ::strtoul(szValue, NULL, 0);
}

//:LLP64 vs LP64
#if (!INT64_EQUAL_LONG)
#if defined(_WIN32)
inline void String2Value(int64_t& i, char const* szValue)
{
    i = ::_strtoi64(szValue, NULL, 0);
}
inline void String2Value(uint64_t& u, char const* szValue)
{
    u = ::_strtoui64(szValue, NULL, 0);
}
#else
inline void String2Value(int64_t& i, char const* szValue)
{
    //:may be strtoq
    i = ::strtoll(szValue, NULL, 0);
}
inline void String2Value(uint64_t& u, char const* szValue)
{
    //:may be strtouq
    u = ::strtoull(szValue, NULL, 0);
}
#endif
#endif

inline void String2Value(float& f, char const* szValue)
{
    f = (float)atof(szValue);
}
inline void String2Value(double& d, char const* szValue)
{
    d = atof(szValue);
}
inline void String2Value(std::string& s, char const* szValue)
{
    s = szValue;
}
//__________________________________________________________________________________
inline char const* Value2String(bool b, std::string& tmp)
{
    return b ? "true" : "false";
}
inline char const* Value2String(char ch, std::string& tmp)
{
    tmp.resize(1);
    tmp[0] = ch;
    return tmp.c_str();
}
inline char const* Value2String(int i, std::string& tmp)
{
    int n = std::numeric_limits<int>::digits10;
    tmp.resize(n + 1);
    sprintf(&tmp[0], "%d", i);
    tmp = tmp.c_str();
    return tmp.c_str();
}
inline char const* Value2String(unsigned int u, std::string& tmp)
{
    int n = std::numeric_limits<unsigned int>::digits10;
    tmp.resize(n + 1);
    sprintf(&tmp[0], "%u", u);
    tmp = tmp.c_str();
    return tmp.c_str();
}
inline char const* Value2String(long i, std::string& tmp)
{
    int n = std::numeric_limits<long>::digits10;
    tmp.resize(n + 1);
    sprintf(&tmp[0], "%ld", i);
    tmp = tmp.c_str();
    return tmp.c_str();
}
inline char const* Value2String(unsigned long u, std::string& tmp)
{
    int n = std::numeric_limits<unsigned long>::digits10;
    tmp.resize(n + 1);
    sprintf(&tmp[0], "%lu", u);
    tmp = tmp.c_str();
    return tmp.c_str();
}
//:LLP64 vs LP64
#if (!INT64_EQUAL_LONG)
inline char const* Value2String(int64_t i, std::string& tmp)
{
    int n = std::numeric_limits<int64_t>::digits10;
    tmp.resize(n + 1);
    //:may be %q
    sprintf(&tmp[0], "%lld", i);
    tmp = tmp.c_str();
    return tmp.c_str();
}
inline char const* Value2String(uint64_t u, std::string& tmp)
{
    int n = std::numeric_limits<uint64_t>::digits10;
    tmp.resize(n + 1);
    //:may be %q
    sprintf(&tmp[0], "%llu", u);
    tmp = tmp.c_str();
    return tmp.c_str();
}
#endif
inline char const* Value2String(float f, std::string& tmp)
{
    tmp.resize(64);//TODO fix me
    sprintf(&tmp[0], "%.7g", f);
    tmp = tmp.c_str();
    return tmp.c_str();
}
inline char const* Value2String(double d, std::string& tmp)
{
    tmp.resize(64);//TODO fix me
    sprintf(&tmp[0], "%.15g", d);
    tmp = tmp.c_str();
    return tmp.c_str();
}
inline char const* Value2String(std::string& s, std::string&)
{
    return s.c_str();
}


//:TODO fix me: validation support
class abstract_validator
{};

template<typename T>
struct is_validator
{
private:
    struct true_t { char dummy[1]; };
    struct false_t { char dummy[2]; };
    static true_t test(abstract_validator const*);
    static false_t test(...);
    static T const* get_T();
public:

    enum { result = (sizeof(test(get_T())) == sizeof(true_t)) };
};

template<typename T, class D>
struct simple_validator
{
    static void set_Default(T& t, D const& d)
    {
        t = d;
    }
};

template<typename T, class D>
struct simple_validator<std::vector<T>, D>
{
    static void set_Default(std::vector<T>& v, D const& d)
    {
        v.clear();
    }
};

struct validation_handler
{
    template<typename T, class D>
    static void set_Default(T& t, D const& d)
    {
        set_Default(t, d, int2type<is_validator<D>::result>());
    }

    template<typename T, class D>
    static void set_Default(T& t, D const& d, int2type<0> const&)
    {
        simple_validator<T, D>::set_Default(t, d);
    }

    template<typename T, class D>
    static void set_Default(T& t, D const& d, int2type<1> const&)
    {
        d.set_Default(t);
    }

    template<typename T, class D>
    static void from_String(T& t, char const* szName, char const* szValue, D const& d)
    {
        from_String(t, szName, szValue, d, int2type<is_validator<D>::result>());
    }

    template<typename T, class D>
    static void from_String(T& t, char const* szName, char const* szValue, D const& d, int2type<0> const&)
    {
        String2Value(t, szValue);
    }

    template<typename T, class D>
    static void from_String(T& t, char const* szName, char const* szValue, D const& d, int2type<1> const&)
    {
        String2Value(t, szValue);
        if (d.invalid(t))
        {
#if defined(LOG_BAD_DATA)
#if (1 == LOG_BAD_DATA)
            std::cout << "WARNING: invalid data: " << szName << "='" << szValue << "'" << std::endl;
#endif
#endif
            d.set_Default(t);
        }
    }
};
//__________________________________________________________________________________
template<typename T = int>
class numeric_validator:
    public abstract_validator
{
    T def_;
    T min_;
    T max_;
public:
    explicit numeric_validator(T adef, T amin, T amax):
        def_(adef),
        min_(amin),
        max_(amax)
    {}
//.............................................................................
    bool invalid(T t) const 
    {
        return t < min_ || t > max_;
    }
//.............................................................................
    void set_Default(T& t) const
    {
        t = def_;
    }
};
//__________________________________________________________________________________
template<typename T>
inline numeric_validator<T> validator(T adef, T amin, T amax)
{
    return numeric_validator<T>(adef, amin, amax);
}
//__________________________________________________________________________________
template<typename T, typename D>
inline void Set_Default(T& t, D const& d)//TODO kill it
{
    validation_handler::set_Default(t, d);
}
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
template<typename T, typename D>
inline void From_String(T& t, char const* szName, char const* szValue, D const& d)//TODO kill it
{
    validation_handler::from_String(t, szName, szValue, d);
}
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
class def:
    public visitor<def>
{
public:
    template<class T>
    def& operator >>(T& t)
    {
        visit_T(t, T::get_Section());
        return *this;
    }

    template<typename T, typename D>
    void visit_Class(T& t, char const* szName, D const&)
    {
        t.accept(*this);
    }

    template<typename T, typename D>
    void visit(T& t, char const* szName, D const& d)
    {
        meta::Set_Default(t, d);
    }

    template<typename T, typename D>
    void visit_Array(T* pt, size_t N, char const* szName, D const& d)
    {
        ASSERT_(*szName);
        for (size_t i = 0; i < N; ++i)
        {
            visit_T(pt[i], "/", d);
        }
    }

    template<typename T, typename D>
    void visit_Ptr(T* pt, char const* szName, D const& d)
    {}
};
//__________________________________________________________________________________
inline def& set_default_values()
{
    static def d;
    return d;
}
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
}//meta
//__________________________________________________________________________________

//EOF!
